/**
 * Copyright (c) 2018 qar All rights reserved.
 *
 * https://www.qar.io
 *
 * 版权所有，侵权必究！
 */

package com.example.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.converters.longconverter.LongStringConverter;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.handler.context.RowWriteHandlerContext;
import com.alibaba.excel.write.handler.context.SheetWriteHandlerContext;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.BeanUtils;

import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Excel工具类
 *
 * @author Mark sunlightcs@gmail.com
 * @since 1.0.0
 */
public class ExcelUtils {

    //表头样式
    public static final WriteCellStyle headWriteCellStyle = new WriteCellStyle();

    static {
        // 表头背景设置为蓝色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
        WriteFont contentWriteFont = new WriteFont();
        // 字体加粗
        contentWriteFont.setBold(true);
        contentWriteFont.setFontHeightInPoints((short) 10);
        headWriteCellStyle.setWriteFont(contentWriteFont);

    }

    /**
     * Excel导出
     *
     * @param response      response
     * @param fileName      文件名
     * @param sheetName     sheetName
     * @param list          数据List
     * @param pojoClass     对象Class
     */
    public static void exportExcel(HttpServletResponse response, String fileName, String sheetName, List<?> list,
                                   Class<?> pojoClass) throws IOException {
        if(StringUtils.isBlank(fileName)){
            //当前日期
            fileName = DateUtils.format(new Date());
        }

        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("UTF-8");
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" +  fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), pojoClass).registerConverter(new LongStringConverter()).sheet(sheetName).doWrite(list);
    }

    /**
     * Excel导出，先sourceList转换成List<targetClass>，再导出
     *
     * @param response      response
     * @param fileName      文件名
     * @param sheetName     sheetName
     * @param sourceList    原数据List
     * @param targetClass   目标对象Class
     */
    public static void exportExcelToTarget(HttpServletResponse response, String fileName, String sheetName, List<?> sourceList,
                                           Class<?> targetClass) throws Exception {
        List targetList = new ArrayList<>(sourceList.size());
        for(Object source : sourceList){
            Object target = targetClass.newInstance();
            BeanUtils.copyProperties(source, target);
            targetList.add(target);
        }

        exportExcel(response, fileName, sheetName, targetList, targetClass);
    }

    /**
     * Excel导出，List<Map<String,String>>
     *
     * @param response      response
     * @param fileName      文件名
     * @param sheetName     sheetName
     * @param headList      数据头list
     * @param headMap      显示头map
     * @param dataList      数据List
     * @param highLightString 高亮显示行对应的第一列数据
     */
    public static void exportExcelByMap(HttpServletResponse response, String fileName, String sheetName, List<String> headList, Map<String,String> headMap, List<Map<String,String>> dataList, Integer highLightString) throws Exception {
        if(StringUtils.isBlank(fileName)){
            //当前日期
            fileName = DateUtils.format(new Date());
        }

        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("UTF-8");
        fileName = URLEncoder.encode(fileName + "_" + DateUtil.format(new Date(),"yyyyMMddHHmm"), "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" +  fileName + ".xlsx");

        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy();
        horizontalCellStyleStrategy.setHeadWriteCellStyle(headWriteCellStyle);

        EasyExcel.write(response.getOutputStream())
                .head(head(headMap, headList))
                .registerWriteHandler(horizontalCellStyleStrategy) //指定样式
                .registerWriteHandler(new SheetWriteHandler() {
                    @Override
                    public void afterSheetCreate(SheetWriteHandlerContext writeContext) {
                        // 将设置好的列宽应用到WriteSheet
                        Workbook workbook = writeContext.getWriteWorkbookHolder().getWorkbook();
                        Sheet sheet = workbook.getSheet(sheetName);
                        for (int i = 0; i < headList.size(); i++) {
                            sheet.setColumnWidth(i, 15 * 256);
                        }

                    }
                })
                // 行写入时，进行特殊处理，即高亮处理
                .registerWriteHandler(new RowWriteHandler() {
                    @Override
                    public void afterRowDispose(RowWriteHandlerContext context) {
                        //行头数据跳过 获取没有需要高亮的数据，略过
                        if (context.getHead() || highLightString == null) {
                            return;
                        }

                        //第一列数据，是rownum,如果符合条件，则进行高亮处理
                        if(StrUtil.isNotEmpty(context.getRow().getCell(0).getStringCellValue()) && Integer.valueOf(context.getRow().getCell(0).getStringCellValue()).equals(highLightString)) {
                            Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
                            context.getRow().cellIterator().forEachRemaining(cell -> {
                                CellStyle writeCellStyle = workbook.createCellStyle();
                                writeCellStyle.setBorderTop(BorderStyle.MEDIUM_DASH_DOT_DOT);//设置边框虚线 上
                                writeCellStyle.setBorderBottom(BorderStyle.MEDIUM_DASH_DOT_DOT);//设置边框虚线 下
                                writeCellStyle.setBorderLeft(BorderStyle.MEDIUM_DASH_DOT_DOT);//设置边框虚线 左
                                writeCellStyle.setBorderRight(BorderStyle.MEDIUM_DASH_DOT_DOT);//设置边框虚线 右
                                writeCellStyle.setTopBorderColor(IndexedColors.RED.index);//设置边框颜色红色 上
                                writeCellStyle.setBottomBorderColor(IndexedColors.RED.index);//设置边框颜色红色 下
                                writeCellStyle.setLeftBorderColor(IndexedColors.RED.index);//设置边框颜色红色 左
                                writeCellStyle.setRightBorderColor(IndexedColors.RED.index);//设置边框颜色红色 右
                                Font writeFont = workbook.createFont();
                                //设置字体颜色，红色
                                writeFont.setColor(IndexedColors.RED.getIndex());
                                //设置字体加粗
                                writeFont.setBold(true);
                                writeCellStyle.setFont(writeFont);
                                cell.setCellStyle(writeCellStyle);
                            });
                        }

                    }
                })
                .registerConverter(new LongStringConverter()).sheet(sheetName)
                .doWrite(dataList(dataList,headList));

    }

    /**
     * Excel导出，List<Map<String,String>>
     *
     * @param response      response
     * @param fileName      文件名
     * @param sheetName     sheetName
     * @param headList      数据头list
     * @param headMap       数据头Map
     * @param dataList      数据List
     */
    public static void exportExcelByMap(HttpServletResponse response, String fileName, String sheetName, List<String> headList, Map<String,String> headMap, List<Map<String,String>> dataList) throws Exception {
        if(StringUtils.isBlank(fileName)){
            //当前日期
            fileName = DateUtils.format(new Date());
        }

        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("UTF-8");
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" +  fileName + ".xlsx");

        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy();
        horizontalCellStyleStrategy.setHeadWriteCellStyle(headWriteCellStyle);

        EasyExcel.write(response.getOutputStream())
                .head(head(headList, headMap))
                .registerWriteHandler(horizontalCellStyleStrategy) //指定样式
                .registerConverter(new LongStringConverter()).sheet(sheetName)
                .doWrite(dataList(dataList,headList));

    }

    /**
     * 从List<String> 中获取标题LIST
     * @param headMap
     * @param headList
     * @return
     */
    private static List<List<String>> head(Map<String,String> headMap, List<String> headList) {
        List<List<String>> list = new ArrayList<>();
        for (String head : headList) {
            List<String> one = new ArrayList<>();
            one.add(headMap.get(head) == null ? head:headMap.get(head));
            list.add(one);
        }
        return list;
    }

    /**
     * 从Map<String,String> 中获取标题LIST
     * @param headList      数据头list
     * @param headMap       数据头Map
     * @return
     */
    private static List<List<String>> head(List<String> headList, Map<String,String> headMap) {
        List<List<String>> list = new ArrayList<>();
        for (String head : headList) {
            List<String> one = new ArrayList<>();
            one.add(headMap.get(head));
            list.add(one);
        }
        return list;
    }

    /**
     * 从List<Map<String,String>> 中获取数据LIST
     * @param dataList
     * @param headList
     * @return
     */
    private static List<List<String>> dataList(List<Map<String, String>> dataList, List<String> headList) {
        List<List<String>> list = new ArrayList<>();
        for (Map<String, String> map : dataList) {
            List<String> data = new ArrayList<>();
            for (int i = 0; i < headList.size(); i++) {
                data.add(map.get(headList.get(i)));
            }
            list.add(data);
        }
        return list;
    }
}
